// chanscan utilizes a channel to implement a 'wheel':
// recieving from the channel also sends the value to it
package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

type mydat struct {
	d time.Time
	n int
}

var values chan mydat
var wg sync.WaitGroup

const wheelSize = 10

// Add drops the oldest value from the value channel and sends a new
// one to it.
func Add(vals chan mydat, dat mydat) {
	wg.Wait()
	<-vals
	vals <- dat
}

// Scan receives all the values from the value channel, stores them
// for later use and re-sends the values back to the channel. Other go
// routines that manipulate the value channel should wait for the
// waitgroup this function increments.
func Scan(vals chan mydat) {
	wg.Add(1)
	defer wg.Done()

	l := len(vals)
	for k := 0; k < l; k++ {
		v := <-vals
		// TODO use v
		fmt.Print(v.n, " ")
		vals <- v
	}
	fmt.Println("")
}

// regularAdd adds a value to the value channel at regular time
// intervals.
func regularAdd(vals chan mydat) {
	i := 1

	for {
		select {
		case <-time.Tick(500 * time.Millisecond):
			v := mydat{d: time.Now(), n: i}
			Add(vals, v)
			i++
			i = i % 10
		}
	}

}

// irregularScan processes the contents of the value channel at
// unpredictable times.
func irregularScan(vals chan mydat) {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	for {
		select {
		case <-time.After(time.Duration(r.Intn(500)) * time.Millisecond):
			Scan(vals)
		}
	}

}

func main() {
	valchan := make(chan mydat, wheelSize)
	for i := 0; i < wheelSize; i++ {
		valchan <- mydat{d: time.Now(), n: 0}
	}

	go regularAdd(valchan)
	go irregularScan(valchan)

	time.Sleep(60 * time.Second)
	wg.Wait()
}
